package org.agony2d.core {
	import flash.display.Stage;
	import flash.events.Event;
	import flash.utils.Dictionary;
	import flash.utils.getTimer;
	import org.agony2d.debug.Logger;
	import org.agony2d.notify.AEvent;
	import org.agony2d.notify.Notifier;

	use namespace agony_internal
	
public class UpdaterManager {
	
	agony_internal static function set running( b:Boolean ) : void {
		if (g_running != b) {
			g_running = b
			if (b) {
				g_stage.addEventListener(Event.ENTER_FRAME, updateAll, false, 8000, true)
				g_oldTime = getTimer()
			}
			else {
				g_stage.removeEventListener(Event.ENTER_FRAME, updateAll)
			}
			Logger.reportMessage("UpdaterManager", (b ? '▲' : '▼') + 'running [ ' + b + ' ]...')
		}
	}
	
	agony_internal static function addNextUpdater( updater:INextUpdater ) : void {
		g_nextUpdaterList[g_nextUpdaterLength++] = updater
	}
	
	agony_internal static function removeNextUpdater( updater:INextUpdater ) : void {
		var index:int
		
		if (g_execLength > 0) {
			index = g_execList.indexOf(updater)
			if (index > g_execIndex) {
				g_execList[index] = g_execList[--g_execLength]
				g_execList.pop()
			}
		}
		else {
			index = g_nextUpdaterList.indexOf(updater)
			g_nextUpdaterList[index] = g_nextUpdaterList[--g_nextUpdaterLength]
			g_nextUpdaterList.pop()
		}
	}
	
	/** new frame updater goes into the queue at next frame... */
	agony_internal static function addFrameUpdater( updater:IFrameUpdater, priority:int ) : void {
		var UP_A:UpdaterProp
		
		if (g_updaterList[updater]) {
			Logger.reportError("UpdaterManager", "addFrameUpdater", "Updater already exists...")
		}
		UP_A           =  new UpdaterProp
		UP_A.updater   =  updater
		UP_A.priority  =  priority
		g_updaterList[updater] = cachedWaitingList[cachedWaitingLength++] = UP_A
	}
	
	agony_internal static function removeFrameUpdater( updater:IFrameUpdater ) : void {
		var UP_A:UpdaterProp 
		var index:int
		
		UP_A = g_updaterList[updater]
		if (!UP_A) {
			Logger.reportError("UpdaterManager", "removeUpdater", "Updater does not exists...!!");
		}
		delete g_updaterList[updater]
		if (!UP_A.enqueued) {
			index = cachedWaitingList.indexOf(UP_A)
			cachedWaitingList[index] = cachedWaitingList[--cachedWaitingLength]
			cachedWaitingList.pop()
		}
		else {
			if (UP_A == g_curr) {
				g_curr = UP_A.prev
			}
			UP_A.prev.next = UP_A.next
			if (UP_A.next) {
				UP_A.next.prev = UP_A.prev
			}
		}
	}
	
	agony_internal static function updateAll( e:Event = null ) : void {
		var t:int, priority:int, l:int
		var AY:Array
		var UP_A:UpdaterProp, UP_B:UpdaterProp
		
		t          =  getTimer()
		g_elapsed  =  (t - g_oldTime) * g_timeScaleFactor
		g_oldTime  =  t
		
		// next frame modify
		if (g_nextUpdaterLength > 0) {
			AY                 =  g_nextUpdaterList
			g_nextUpdaterList  =  g_execList
			g_execList         =  AY
			g_execLength       =  g_nextUpdaterLength
			g_nextUpdaterList.length = g_nextUpdaterLength = 0
			while (g_execIndex < g_execLength) {
				g_execList[g_execIndex++].modify()
			}
			g_execIndex = g_execLength = g_execList.length = 0
		}
		
		// frame by frame
		if(cachedWaitingLength > 0) {
			l = cachedWaitingLength
			while (--l > -1) {
				UP_A           =  cachedWaitingList[l]
				UP_A.enqueued  =  true
				if (!g_head.next) {
					UP_A.prev    =  g_head
					g_head.next  =  UP_A
				}
				else if (UP_A.priority >= g_head.next.priority) {
					UP_A.prev    =  g_head
					UP_A.next    =  g_head.next
					g_head.next  =  g_head.next.prev  =  UP_A
				}
				else {
					UP_B      =  g_head.next
					priority  =  UP_A.priority;
					while(UP_B.next && priority < UP_B.next.priority) {
						UP_B = UP_B.next
					}
					UP_A.prev = UP_B
					if (UP_B.next) {
						UP_A.next  =  UP_B.next
						UP_B.next  =  UP_B.next.prev  =  UP_A
					}
					else {
						UP_B.next  =  UP_A
					}
				}
			}
			cachedWaitingList.length = cachedWaitingLength = 0
		}
		g_curr = g_head.next
		while (g_curr) {
			g_curr.updater.updateFrameByFrame()
			g_curr = g_curr.next
		}
		
		// enter frame event
		g_globalNotifier.dispatchDirectEvent(AEvent.ENTER_FRAME)
	}
	
	
	// priority
	agony_internal static const KEYBOARD:int   =  90000
	agony_internal static const INTERACT:int   =  22000
	agony_internal static const DELAY:int      =  8000
	agony_internal static const TIMER:int      =  5000
	agony_internal static const TWEEN:int      =  1200
	agony_internal static const ANIMATION:int  =  420
	agony_internal static const GPU:int        =  110
	
	// next frame
	agony_internal static var g_nextUpdaterList:Array = []
	agony_internal static var g_execList:Array = []
	agony_internal static var g_nextUpdaterLength:int, g_execLength:int, g_execIndex:int
	
	// frame by frame
	agony_internal static var g_stage:Stage
	agony_internal static var g_elapsed:int
	agony_internal static var g_running:Boolean
	agony_internal static var g_timeScaleFactor:Number = 1
	agony_internal static var g_oldTime:int
	agony_internal static var g_globalNotifier:Notifier = new Notifier
	
	agony_internal static var cachedWaitingList:Vector.<UpdaterProp> = new <UpdaterProp>[]	// waiting add...
	agony_internal static var cachedWaitingLength:int
	agony_internal static var g_updaterList:Dictionary = new Dictionary  // IFrameUpdater : UpdaterProp
	agony_internal static var g_curr:UpdaterProp
	agony_internal static var g_head:UpdaterProp = new UpdaterProp
}
}